package com.denlaku.longan.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.RecursiveTask;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author tianx
 */
@Slf4j
public class ForkJoinSupplierTask<V> extends RecursiveTask<List<V>> {
    private final List<Supplier<V>> suppliers;
    private final boolean throwException;

    public ForkJoinSupplierTask(List<Supplier<V>> suppliers, boolean throwException) {
        this.suppliers = suppliers;
        this.throwException = throwException;
    }

    private ForkJoinSupplierTask<V> of(Supplier<V> supplier) {
        return new ForkJoinSupplierTask<>(Lists.of(Collections.singleton(supplier)), throwException);
    }

    @Override
    protected List<V> compute() {
        if (CollectionUtils.isEmpty(suppliers)) {
            return Lists.empty();
        }
        int size = suppliers.size();
        if (size == 1) {
            V v = doCompute(suppliers.get(0));
            return Lists.of(Collections.singleton(v));
        } else {
            List<ForkJoinSupplierTask<V>> tasks = Lists.toList(suppliers, this::of);
            invokeAll(tasks);
            return tasks.stream().map(ForkJoinSupplierTask::join).flatMap(List::stream).collect(Collectors.toList());
        }
    }

    private V doCompute(Supplier<V> supplier) {
        try {
            return supplier.get();
        } catch (Exception e) {
            log.error("Failed to get data", e);
            if (throwException) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }
}
